﻿using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

using Kenly.DBFramework.Reflection;
using Kenly.DBFramework;
using Kenly.DBFramework.Gateway;

namespace Kenly.DBFramework.Gateway
{
    /// <summary>
    /// The gateway to a database table.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class SqlTableGatewayUtility<T> : TableGatewayUtilityBase<T> where T : TableGateway<T>, new()
    {

        public SqlTableGatewayUtility(DatabaseType databaseType, string connectionString):base(databaseType,connectionString)
        {
        }


        public override int AddNew(ITableCommandExecutor tableCommandExecutor, ICommandReflector commandReflector, T entity, bool returnIdentity)
        {
            if (entity == null)
            {
                return 0;
            }

            List<string> propertyNames = entity.ChangedPropertyNames;
            if (propertyNames == null || propertyNames.Count <= 0)
            {
                return 0;
            }
            List<IDataParameter> paras = ParameterReflection.Insert(base.DatabaseType, entity, propertyNames);
            if (returnIdentity)
            {
                IDataParameter idParameter = ParameterReflection.GetIdentityParameter(base.DatabaseType, entity, true);
                paras.Add(idParameter);
            }
            string insertStatement = commandReflector.Insert(typeof(T), returnIdentity, propertyNames);
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(insertStatement, false, paras);
            if (affectedRowCount > 0 && returnIdentity)
            {
                entity.ClearChangedPropertyNames();
                UpdateIdentity(entity, paras);
            }
            return affectedRowCount;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="paras"></param>
        private static void UpdateIdentity(T entity, List<IDataParameter> paras)
        {
            string identityName = IdentityUtility.GetIdentityName(entity.GetType());
            foreach (IDataParameter para in paras)
            {
                if (para.ParameterName == identityName)
                {
                    IdentityUtility.SetIdentityValue(entity, para.Value);
                    return;
                }
            }
        }

        protected override int AddNew(T entity, bool returnIdentity, bool commitTransaction, out Transaction transaction)
        {
            transaction = null;
            if (entity == null)
            {
                return 0;
            }

            List<string> propertyNames = entity.ChangedPropertyNames;
            if (propertyNames == null || propertyNames.Count <= 0)
            {
                return 0;
            }
            List<IDataParameter> paras = ParameterReflection.Insert(base.DatabaseType, entity, propertyNames);
            if (returnIdentity)
            {
                IDataParameter idParameter = ParameterReflection.GetIdentityParameter(base.DatabaseType, entity, true);
                paras.Add(idParameter);
            }

            ICommandReflector commandReflector = base.CreateCommandReflector();
            ITableCommandExecutor tableCommandExecutor = base.CreateCommandExecutor();
            transaction = new Transaction(tableCommandExecutor);
            string insertStatement = commandReflector.Insert(typeof(T), returnIdentity, propertyNames);

            if (!commitTransaction)
            {
                tableCommandExecutor.BeginTransaction();
            }
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(insertStatement, false, paras);
            if (affectedRowCount > 0 && returnIdentity)
            {
                entity.ClearChangedPropertyNames();
                UpdateIdentity(entity, paras);
            }
            return affectedRowCount;
        }

        protected override int AddNew(IList<T> entities, bool returnIdentity, bool commitTransaction, out Transaction transaction)
        {
            transaction = null;
            if (entities == null || entities.Count <= 0)
            {
                return 0;
            }

            string insertStatement = "";
            List<IDataParameter> paras = null;
            List<string> propertyNames;
            int affectedRowCount = 0;

            ICommandReflector commandReflector = CreateCommandReflector();
            ITableCommandExecutor tableCommandExecutor = CreateCommandExecutor();
            transaction = new Transaction(tableCommandExecutor);

            try
            {
                tableCommandExecutor.BeginTransaction();
                foreach (T entity in entities)
                {
                    propertyNames = entity.ChangedPropertyNames;
                    if (propertyNames == null || propertyNames.Count <= 0)
                    {
                        continue;
                    }
                    paras = ParameterReflection.Insert(base.DatabaseType, entity, propertyNames);
                    if (returnIdentity)
                    {
                        IDataParameter idParameter = ParameterReflection.GetIdentityParameter(base.DatabaseType, entity, true);
                        paras.Add(idParameter);
                    }
                    insertStatement = commandReflector.Insert(typeof(T), returnIdentity, propertyNames);
                    if (tableCommandExecutor.ExecuteNonQuery(insertStatement, false, paras) > 0)
                    {
                        affectedRowCount++;
                        if (returnIdentity)
                        {
                            UpdateIdentity(entity, paras);
                        }
                        entity.ClearChangedPropertyNames();
                    }
                }
                if (commitTransaction)
                {
                    tableCommandExecutor.Commit();
                }
                return affectedRowCount;
            }
            catch (Exception ex)
            {
                tableCommandExecutor.Rollback();
                throw new CommandException(insertStatement, MsgResource.ExecuteNonQueryFailed, ex);
            }
        }

       
    }


}